/*
Autor: Alexander Savochkin, 2010
New BSD License
*/

#pragma once

#include <vector>
#include <string>
#include <boost/optional.hpp>
#include <boost/variant.hpp>

#include "ClientInfo.h"
#include "Channel.h"
#include "Response.h"
#include "Manager.h"

using boost::optional;
using boost::variant;

//---------------------------------------------
//Commands syntax tree
//---------------------------------------------

namespace SRCLF 
{
namespace ClientRequestParsing
{
namespace AST
{
	struct InputGetCommand {};
	struct InputAdminCommand 
	{
		string value;
	};
	struct InputControlCommand
	{
		string value;
	};
	struct InputInfoCommand
	{
		string value;
	};

	typedef variant<InputGetCommand, 
		InputAdminCommand, 
		InputControlCommand, 
		InputInfoCommand> InputCommand;

	class InputCommandVisitor : public boost::static_visitor<>
	{
		IOperatedInput *input;
		Manager *manager;
		//ClientId client_id;
		const ClientInfo* client_info;
		string current_channel_name;
		string &result;
	public:
		InputCommandVisitor(IOperatedInput *_input, Manager *_manager, 
			const ClientInfo* _client_info, const string& _current_channel_name, string& _result) : 
		input(_input), manager(_manager), client_info(_client_info), current_channel_name(_current_channel_name),
			result(_result) {}

		void operator() (const InputGetCommand& cmd)
		{
			double input_value = input->getInput();
			Response::onGetInput(result,input_value); //Get input value
		}

		void operator() (const InputAdminCommand& cmd)
		{
			//User should have "control" privelegies to perform this action
			if(manager->checkAdminOnChannel(current_channel_name, client_info, result))
			{
				input->adminCommand(cmd.value, result);
			}
		}

		void operator() (const InputControlCommand& cmd)
		{
			//User should have "control" privelegies to perform this action
			if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			{
				input->controlCommand(cmd.value, result);
			}
		}

		void operator() (const InputInfoCommand& cmd)
		{
			input->infoCommand(cmd.value, result);
		}
	};

	//----------------------------------------------------------

	struct OutputGetCommand {};
	struct OutputSetCommand {
		double value;
	};
	struct OutputAdminCommand 
	{
		string value;
	};
	struct OutputControlCommand
	{
		string value;
	};
	struct OutputInfoCommand
	{
		string value;
	};

	typedef variant<OutputSetCommand,
		OutputGetCommand,
		OutputAdminCommand, 
		OutputControlCommand, 
		OutputInfoCommand> OutputCommand;

	class OutputCommandVisitor : public boost::static_visitor<>
	{
		IOperatedOutput *output;
		Manager *manager;
		const ClientInfo *client_info;
		string current_channel_name;
		string &result;
	public:

		OutputCommandVisitor(IOperatedOutput *_output, Manager *_manager, 
			const ClientInfo *_client_info, const string& _current_channel_name, string& _result) : 
		output(_output), manager(_manager),client_info(_client_info),current_channel_name(_current_channel_name),
			result(_result) 
		{}

		void operator() (const OutputSetCommand& cmd)
		{
			//User should have "control" privelegies to perform this action
			if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			{	
				output->setOutput(cmd.value);
				Response::onSetOutput(result);
			}
		}

		void operator() (const OutputGetCommand& cmd)
		{
			//User should have "control" privelegies to perform this action
			double output_value = output->getOutput();
			Response::onGetOutput(result,output_value); //Get output value
		}

		void operator() (const OutputAdminCommand& cmd)
		{
			//User should have "control" privelegies to perform this action
			if(manager->checkAdminOnChannel(current_channel_name, client_info, result))
			{
				output->adminCommand(cmd.value, result);
			}
		}

		void operator() (const OutputControlCommand& cmd)
		{
			//User should have "control" privelegies to perform this action
			if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			{
				output->controlCommand(cmd.value, result);
			}
		}

		void operator() (const OutputInfoCommand& cmd)
		{
			output->infoCommand(cmd.value, result);
		}
	};

	//----------------------------------------------------------
	struct RegulatorAdminCommand 
	{
		string value;
	};
	struct RegulatorControlCommand 
	{
		string value;
	};
	struct RegulatorInfoCommand 
	{
		string value;
	};

	typedef variant<RegulatorAdminCommand,
			RegulatorControlCommand, 
			RegulatorInfoCommand> RegulatorCommand;

	//----------------------------------------------------------
	class RegulatorCommandVisitor : public boost::static_visitor<>
	{
		IRegulator *regulator;
		Manager *manager;
		const ClientInfo *client_info;
		string current_channel_name;
		string &result;
	public:
		RegulatorCommandVisitor(IRegulator *_regulator, Manager *_manager, 
			const ClientInfo *_client_info, const string& _current_channel_name, string& _result) : 
		regulator(_regulator), manager(_manager),client_info(_client_info),current_channel_name(_current_channel_name),
			result(_result) 
		{}

		  void operator() (const RegulatorAdminCommand& cmd)
		  {
			  //User should have "control" privelegies to perform this action
			  if(manager->checkAdminOnChannel(current_channel_name, client_info, result))
			  {
				  regulator->adminCommand(cmd.value, result);
			  }
		  }

		  void operator() (const RegulatorControlCommand& cmd)
		  {
			  //User should have "control" privelegies to perform this action
			  if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			  {
				  //User has enough access rights
				  regulator->controlCommand(cmd.value, result);
			  }
		  }

		  void operator() (const RegulatorInfoCommand& cmd)
		  {
			  regulator->infoCommand(cmd.value, result);
		  }
	};

	struct ChannelInputCommand 
	{
		InputCommand input_command;
	};
	struct ChannelOutputCommand
	{
		OutputCommand cmd;
	};
	struct ChannelRegulatorCommand
	{
		RegulatorCommand cmd;
	};

	struct ChannelDisableCommand {};
	struct ChannelKeepCommand 
	{
		ChannelKeepCommand() {}
		ChannelKeepCommand(optional<double> _value) :  value(_value) {}
		ChannelKeepCommand(double _value) :  value(_value) {}
		optional<double> value;
	};

	struct ChannelCaptureControlCommand {
		ChannelCaptureControlCommand() {}
		//ChannelCaptureControlCommand( const std::string::iterator& it1, const std::string::iterator& it2 ) {}	//magic?
	};
	struct ChannelReleaseControlCommand {};


	typedef variant<ChannelInputCommand, 
	ChannelOutputCommand,
	ChannelDisableCommand,
	ChannelKeepCommand,
	ChannelRegulatorCommand,
	ChannelCaptureControlCommand,
	ChannelReleaseControlCommand> ChannelCommands;


	class ChannelCommandVisitor : public boost::static_visitor<>
	{
		Channel* channel;
		Manager *manager;
		const ClientInfo *client_info;
		string current_channel_name;
		string &result;

	public:
		ChannelCommandVisitor( const string& _current_channel_name, Manager *_manager, const ClientInfo *_client_info, string& _result ) :
		  current_channel_name(_current_channel_name), manager(_manager), client_info(_client_info), result(_result) 
		  {
			  channel = manager->getChannelByName(current_channel_name);
		  }

		  void operator() (const ChannelInputCommand& cmd)
		  {
			  IOperatedInput *input = channel->getInput();
			  InputCommandVisitor input_command_visitor(input, manager, client_info, current_channel_name, result);
			  boost::apply_visitor( input_command_visitor, cmd.input_command);
		  }

		  void operator() (const ChannelOutputCommand& cmd)
		  {
			  IOperatedOutput *output = channel->getOutput();
			  OutputCommandVisitor output_command_visitor(output, manager, client_info, current_channel_name, result);
			  boost::apply_visitor( output_command_visitor, cmd.cmd);
		  }

		  void operator() (const ChannelRegulatorCommand& cmd)
		  {
			  IRegulator *regulator = channel->getRegulator();
			  RegulatorCommandVisitor regulator_command_visitor(regulator, manager, client_info, current_channel_name, result);
			  boost::apply_visitor( regulator_command_visitor, cmd.cmd);
		  }

		  void operator() (const ChannelDisableCommand& cmd)
		  {
			  //User should have "control" privelegies to perform this action
			  if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			  {
				  //User has enough access rights
				  channel->disableRegulator();
				  Response::onDisable(result);	//Ok
			  }
		  }

		  void operator() (const ChannelKeepCommand& cmd)
		  {
			  //User should have "control" privelegies to perform this action
			  if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			  {
				  //User has enough access rights
				  double keep_value = channel->keep(cmd.value);
				  Response::onKeep(result,keep_value);	//Ok
			  }
		  }

		  void operator() (const ChannelCaptureControlCommand& cmd)
		  {
			  //User should have "control" privelegies to perform this action
			  if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			  {
				  manager->captureControl(current_channel_name, client_info);
				  Response::onControlCaptured(result);
			  }
		  }

		  void operator() (const ChannelReleaseControlCommand& cmd)
		  {
			  //User should have "control" privelegies to perform this action
			  if(manager->checkControlOnChannel(current_channel_name, client_info, result))
			  {
				  manager->releaseControl(current_channel_name);
				  Response::onControlReleased(result);
			  }
		  }
	};


	//----------------------------------------------------------


	struct ChannelCommand 
	{
		ChannelCommand() {} 
		ChannelCommand(const string& _channel_name, const ChannelCommands& _channel_command) : 
			channel_name(_channel_name), channel_command(_channel_command) {}
		string channel_name;
		ChannelCommands channel_command;
	};

	typedef variant<ChannelCommand> Commands; // <-- Some other commands may appear in future. They will be added into this variant<...> template



	class CommandsVisitor : public boost::static_visitor<>
	{    
		Manager *manager;
		const ClientInfo *client_info;
		string &result;
	public:

		CommandsVisitor( Manager *_manager, const ClientInfo *_client_info, string& _result ) :
		  manager(_manager), client_info(_client_info), result(_result)
		  {
		  }

		  void operator() (const ChannelCommand& cmd) const
		  {
			  Channel *channel = manager->getChannelByName(cmd.channel_name);
			  ChannelCommandVisitor channel_command_visitor(cmd.channel_name, manager, client_info, result);
			  boost::apply_visitor( channel_command_visitor, cmd.channel_command );
		  }
	};

	//TODO: Confusing naming! Command - Commands 
	struct Command 
	{
		Command() {}
		Command(const ChannelCommand& _command) : command(_command) {}
		Command(const Commands& _command) : command(_command) {}
		optional<Commands> command;
	};


	/*
	struct Command 
	{
		Command() {}
		Command(const ChannelCommand& _command) : command(_command) {}	
		optional<ChannelCommand> command;
	};
	*/
} //namespace AST
} //namespace ClientRequestParsing
} //namespace SRCLF
